Skip to content

feat(console/charts): x-axis tick adapts to time-range duration#21

Merged
vaderyang merged 2 commits into
mainfrom
feat/axis-time-multi-day
May 21, 2026
Merged

feat(console/charts): x-axis tick adapts to time-range duration#21
vaderyang merged 2 commits into
mainfrom
feat/axis-time-multi-day

Conversation

@vaderyang
Copy link
Copy Markdown
Collaborator

Why

Every chart had its own copy of `formatAxisTime` that always rendered `HH:MM`. A 7-day window therefore showed ticks like `00:00, 12:00, 00:00, 12:00, ...` — a wrapping clock face with no day attached. Easy to mis-read. Same problem at 24h.

What

Centralize `formatAxisTime(epoch, span)` in `lib/format` and pick the shape from the visible window:

Span Format Example
< 24h `HH:MM` `14:30`
24h–7d `MM-DD HH:MM` `05-12 14:30`
≥ 7d `MM-DD` `05-12`

Each chart derives `span` from its own data (`timestamps[last] - timestamps[0]`), so the formatter has no toolbar coupling and naturally handles partial windows (e.g. tail of a 7d range after retention trimmed the head).

Refactors the four charts that had inline copies:

  • `timeseries-line-chart` (Overview latency, Models, Performance)
  • `request-volume-chart` (Overview)
  • `latency-overview-chart` (Overview)
  • `stacked-bar-chart` (Performance, Traffic)

Verification

  • `bun test` — 103 pass (6 new in `lib/format.test.ts`: each duration bucket, the 24h / 7d inclusive boundaries, single-point fallback)
  • `npm run build` — green
  • Deployed to wuneng (bundle `index-CEHozbh2.js`); confirmed visually that switching toolbar from `1h` → `24h` → `7d` flips axis format accordingly

Test plan

  • Overview `Call Volume` / `Latency Overview` at 1h, 24h, 7d → ticks read as `HH:MM` / `MM-DD HH:MM` / `MM-DD`
  • Performance `TTFT` / `E2E` / `TPOT` charts same
  • Models detail charts same
  • Tooltip on a tick still shows the full timestamp (unchanged — `toLocaleString()`)

🤖 Generated with Claude Code

Vader Yang and others added 2 commits May 15, 2026 17:27
Every chart had its own copy of:

  function formatAxisTime(epoch) {
    const d = new Date(epoch * 1000)
    return `${HH}:${MM}`
  }

Result: a 7-day window rendered ticks as a wrapping clock face
("00:00", "12:00", "00:00", "12:00", ...) with no day attached.
Same problem at 24h. Easy to mis-read.

Centralize the formatter in lib/format as `formatAxisTime(epoch, span)`
and have it pick the right shape based on the visible window:

  span < 24h       →  HH:MM           (5m / 15m / 1h / 6h presets)
  24h ≤ span < 7d  →  MM-DD HH:MM     (24h preset)
  span ≥ 7d        →  MM-DD           (7d preset; time-of-day is noise
                                       when ticks come ~daily)

Each chart derives span from its data (last timestamp − first), so the
formatter requires no toolbar dependency and naturally handles partial
ranges (e.g. tail of a 7d window after retention trimmed the head).

Replaces the inline copies in:
  - timeseries-line-chart  (Overview latency, Models, Performance)
  - request-volume-chart   (Overview)
  - latency-overview-chart (Overview)
  - stacked-bar-chart      (Performance, Traffic)

6 unit tests in lib/format.test.ts cover each duration bucket plus the
24h / 7d inclusive boundaries and the single-point fallback (span = 0
→ HH:MM). Tests assert *shape* not literal values so they pass under
any TZ.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts:
#	console/src/components/charts/timeseries-line-chart.tsx
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Agent run failed (exit 1). See workflow logs.


🤖 Reviewed by viviworkflow run

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my review of the PR diff and all affected files, here is my assessment:

Summary

This PR centralizes chart x-axis time formatting by replacing 4 inline formatAxisTime functions with a single adaptive formatter in lib/format.ts. The formatter adjusts output based on data span (<24h → HH:MM, 24h-7d → MM-DD HH:MM, ≥7d → MM-DD). Six unit tests cover duration buckets and boundary conditions. The change is well-scoped and directly addresses the problem stated in the commit message. APPROVE.

Verified

  • Schema contract: TimeseriesData.timestamps are epoch seconds (i64 in Rust). The formatter correctly multiplies by 1000 for new Date(). Matches the API contract in ts-api/src/routes/metrics.rs:42-44.
  • Import correctness: All 4 chart components now import formatAxisTime from @/lib/format and pass the correct signature (epochSec, spanSec).
  • No duplicate definitions: Grepped for function formatAxisTime — only the centralized export in format.ts:18 remains.
  • Span calculation: All 4 charts compute spanSec identically from lastTs - firstTs with a 0 fallback for single-point data. Matches test coverage.
  • Test coverage: Tests assert shape (regex) rather than literal values, correctly handling TZ independence. Boundary tests at exactly 24h and 7d match the implementation's < comparisons.
  • No build config changes: No changes to package.json, vite.config.ts, or tsconfig.json.

Suggestions

  • console/src/lib/format.ts:26 — The MM-DD format loses year context. For a 7-day window spanning Dec 31 → Jan 1, ticks would show "12-31" → "01-01" without year. This is acceptable for a 7d window, but if longer windows are added later (30d/90d), consider adding year prefix. Not blocking.

  • console/src/lib/format.ts:18-31 — Consider documenting that this uses local timezone (via Date.getHours()/getMonth()). The test file already notes this, but a brief doc comment in the function would help future readers. Non-blocking.


🤖 Reviewed by viviworkflow run

@vaderyang vaderyang merged commit 1785040 into main May 21, 2026
1 check passed
@vaderyang vaderyang deleted the feat/axis-time-multi-day branch May 21, 2026 05:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant